package com.monster.security.data.utils;

import com.monster.security.data.entity.RsaKey;
import com.monster.utils.StringUtils;

import javax.crypto.Cipher;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

/**
 * RSA加密解密
 *
 * @author kuang
 */
public class RsaUtils {
    private RsaUtils() {
    }

    private final static int keySize = 2048;
    private final static String RSA = "RSA";
    private final static String ALGORITHM = "SHA1WithRSA";

    /**
     * 生成随机的秘钥对
     *
     * @param length key的长度
     * @return 秘钥对
     */
    public static RsaKey createRSAKey(Integer length) {
        try {
            // 获得RSA密钥对的生成器实例
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(RSA);
            // 说的一个安全的随机数
            SecureRandom secureRandom = new SecureRandom(String.valueOf(System.currentTimeMillis()).getBytes(StandardCharsets.UTF_8));
            // 这里可以是1024、2048 初始化一个密钥对
            keyPairGenerator.initialize(length == null ? keySize : length, secureRandom);
            // 获得密钥对
            KeyPair keyPair = keyPairGenerator.generateKeyPair();
            // 对秘钥进行编码保存返回
            RsaKey rsaKey = new RsaKey();
            rsaKey.setPrivateKey(Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded()));
            rsaKey.setPublicKey(Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded()));
            return rsaKey;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static RsaKey createRSAKey() {
        return createRSAKey(null);
    }

    /**
     * 将Base64编码后的公钥转换成 PublicKey 对象
     *
     * @param key 秘钥
     * @return PublicKey
     */
    public static PublicKey getPublic(String key) throws NoSuchAlgorithmException, InvalidKeySpecException {
        byte[] bytes = Base64.getDecoder().decode(key);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytes);
        KeyFactory keyFactory = KeyFactory.getInstance(RSA);
        PublicKey publicKey = keyFactory.generatePublic(keySpec);
        return publicKey;
    }

    /**
     * 将Base64编码后的私钥转换成 PrivateKey 对象
     *
     * @param key 秘钥
     * @return PrivateKey
     */
    public static PrivateKey getPrivate(String key) throws NoSuchAlgorithmException, InvalidKeySpecException {
        byte[] bytes = Base64.getDecoder().decode(key);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(bytes);
        KeyFactory keyFactory = KeyFactory.getInstance(RSA);
        PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
        return privateKey;
    }

    /**
     * 公钥加密
     *
     * @param key     加密的key
     * @param content 需要加密的串
     * @return 加密后的数据
     */
    public static String encryptByPublic(String key, String content) {
        try {
            if (StringUtils.isBlank(key) || StringUtils.isBlank(content)) {
                return null;
            }
            Cipher cipher = Cipher.getInstance(RSA);
            cipher.init(Cipher.ENCRYPT_MODE, getPublic(key));
            return Base64.getEncoder().encodeToString(cipher.doFinal(content.getBytes(StandardCharsets.UTF_8)));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 私钥解密
     *
     * @param key     解密的key
     * @param content 需要解密的数据
     * @return 解密后的数据
     */
    public static String decryptByPrivate(String key, String content) {
        try {
            if (StringUtils.isBlank(key) || StringUtils.isBlank(content)) {
                return null;
            }
            Cipher cipher = Cipher.getInstance(RSA);
            cipher.init(Cipher.DECRYPT_MODE, getPrivate(key));
            return new String(cipher.doFinal(Base64.getDecoder().decode(content)), StandardCharsets.UTF_8);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 私钥加密
     *
     * @param key     加密的key
     * @param content 需要加密的串
     * @return 加密后的数据
     */
    public static String encryptByPrivate(String key, String content) {
        try {
            if (StringUtils.isBlank(key) || StringUtils.isBlank(content)) {
                return null;
            }
            Cipher cipher = Cipher.getInstance(RSA);
            cipher.init(Cipher.ENCRYPT_MODE, getPrivate(key));
            return Base64.getEncoder().encodeToString(cipher.doFinal(content.getBytes(StandardCharsets.UTF_8)));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 公钥解密
     *
     * @param key     解密的key
     * @param content 需要解密的数据
     * @return 解密后的数据
     */
    public static String decryptByPublic(String key, String content) {
        try {
            if (StringUtils.isBlank(key) || StringUtils.isBlank(content)) {
                return null;
            }
            Cipher cipher = Cipher.getInstance(RSA);
            cipher.init(Cipher.DECRYPT_MODE, getPublic(key));
            return new String(cipher.doFinal(Base64.getDecoder().decode(content)), StandardCharsets.UTF_8);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * sm2私钥签名
     * @param key 私钥
     * @param content 签名内容
     * @return 签名串
     */
    public static String signByPrivate (String key, String content) {
        try {
            if (StringUtils.isBlank(key) || StringUtils.isBlank(content)) {
                return null;
            }
            Signature signature = Signature.getInstance(ALGORITHM);
            signature.initSign(getPrivate(key));
            signature.update(content.getBytes(StandardCharsets.UTF_8));
            return Base64.getEncoder().encodeToString(signature.sign());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * sm2公钥验签
     * @param key 公钥
     * @param content 原本字符串
     * @param sign 签名字符串
     * @return true/false
     */
    public static boolean verifyByPublic (String key, String content, String sign) {
        try {
            if (StringUtils.isBlank(key) || StringUtils.isBlank(content)) {
                return false;
            }
            Signature signature = Signature.getInstance(ALGORITHM);
            signature.initVerify(getPublic(key));
            signature.update(content.getBytes(StandardCharsets.UTF_8));
            return signature.verify(Base64.getDecoder().decode(sign));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    public static void main(String[] args) {
        String content = "123456789";
        RsaKey keys = createRSAKey();
        System.out.println("公钥：" + keys.getPublicKey());
        System.out.println("私钥：" + keys.getPrivateKey());

        String sign = signByPrivate(keys.getPrivateKey(), content);
        System.out.println("签名串：" + sign);
        System.out.println(verifyByPublic(keys.getPublicKey(), content, sign));
    }

}
